package com.gitee.wsl.ext.string

/**
 *
 *
 * Checks whether the String a valid Java number. Valid numbers include hexadecimal marked with the "0x" qualifier,
 * scientific notation and numbers marked with a type qualifier (e.g. 123L).
 *
 *
 *
 * Null and blank string will return false.
 *
 *
 * @param str the string to check
 * @return true if the string is a correctly formatted number
 */
fun  CharSequence?.isNumber(): Boolean {
    val str = this
    if ((str == null) || (str.isEmpty())) {
        return false
    }
    var sz = str.length
    if (str.get(0) == '0' && str.length > 1) {
        val c = str.get(1)
        if (c != 'e' && c != 'E' && c != 'x' && c != '.') return false
    }

    var hasExp = false
    var hasDecPoint = false
    var allowSigns = false
    var foundDigit = false
    // Deal with any possible sign up front
    val start = if (str.get(0) == '-') 1 else 0
    if (sz > start + 1) {
        if (str.get(start) == '0' && str.get(start + 1) == 'x') {
            var i = start + 2
            if (i == sz) {
                return false // str == "0x"
            }
            // Checking hex (it can't be anything else)
            while (i < sz) {
                val c = str.get(i)
                if ((c !in '0'..'9') && (c !in 'a'..'f') && (c !in 'A'..'F')) {
                    return false
                }
                i++
            }
            return true
        }
    }
    sz-- // Don't want to loop to the last char, check it afterwords
    // for type qualifiers
    var i = start
    // Loop to the next to last char or to the last char if we need another
    // digit to
    // make a valid number (e.g. chars[0..5] = "1234E")
    while (i < sz || (i < sz + 1 && allowSigns && !foundDigit)) {
        val c = str.get(i)
        if (c in '0'..'9') {
            foundDigit = true
            allowSigns = false
        } else if (c == '.') {
            if (hasDecPoint || hasExp) {
                // Two decimal points or dec in exponent
                return false
            }
            hasDecPoint = true
        } else if (c == 'e' || c == 'E') {
            // We've already taken care of hex.
            if (hasExp) {
                // Two E's
                return false
            }
            if (!foundDigit) {
                return false
            }
            hasExp = true
            allowSigns = true
        } else if (c == '+' || c == '-') {
            if (!allowSigns) {
                return false
            }
            allowSigns = false
            foundDigit = false // We need a digit after the E
        } else {
            return false
        }
        i++
    }
    if (i < str.length) {
        val c = str.get(i)
        if (c in '0'..'9') {
            // No type qualifier, OK
            return true
        }
        if (c == 'e' || c == 'E') {
            // Can't have an E at the last byte
            return false
        }
        if (!allowSigns && (c == 'd' || c == 'D' || c == 'f' || c == 'F')) {
            return foundDigit
        }
        if (c == 'l' || c == 'L') {
            // Not allowing L with an exponoent
            return foundDigit && !hasExp
        }

        if (c == '.') return !hasDecPoint && foundDigit && !allowSigns
    }
    // allowSigns is true iff the val ends in 'E'
    // Found digit it to make sure weird stuff like '.' and '1E-' doesn't
    // pass
    return false // !allowSigns && foundDigit;
}

fun String.toIntOrNull() = try {
     toInt()
} catch (e: Exception) {
    null
}